﻿namespace Network
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;

    using Shared;

    public class Network : BaseModel
    {
        private readonly IAlgorithm algorithm;

        private readonly double kilobytesToTransmit;

        private readonly int numberOfClients;

        private readonly double networkSpeedInKilobytesPerSeconds;

        private int succesfulTransmissions;

        private int failedTransmissions;

        private double totalElapsedTime;

        public Network(IAlgorithm algorithm, double kilobytesToTransmit, int numberOfClients, double networkSpeedInKilobytesPerSeconds)
        {
            this.SuccesfulTransmissions = 0;
            this.FailedTransmissions = 0;
            this.TotalElapsedTime = 0;

            this.algorithm = algorithm;
            this.kilobytesToTransmit = kilobytesToTransmit;
            this.algorithm.SetupFrames(kilobytesToTransmit);
            this.numberOfClients = numberOfClients;
            this.networkSpeedInKilobytesPerSeconds = networkSpeedInKilobytesPerSeconds;
        }

        public int SuccesfulTransmissions
        {
            get
            {
                return this.succesfulTransmissions;
            }

            private set
            {
                this.succesfulTransmissions = value;
                this.OnPropertyChanged("SuccesfulTransmissions");
            }
        }

        public int FailedTransmissions
        {
            get
            {
                return this.failedTransmissions;
            }

            private set
            {
                this.failedTransmissions = value;
                this.OnPropertyChanged("FailedTransmissions");
            }
        }

        public double TotalElapsedTime
        {
            get
            {
                return this.totalElapsedTime;
            }

            private set
            {
                this.totalElapsedTime = value;
                this.OnPropertyChanged("TotalElapsedTime");
            }
        }

        public double Bandwith
        {
            get
            {
                return this.kilobytesToTransmit / this.TotalElapsedTime;
            }
        }

        public void Start(Action callback, Action<SimulationProgress> progressCallback)
        {

            BackgroundWorker worker = new BackgroundWorker { WorkerReportsProgress = true };
            worker.RunWorkerCompleted += (sender, e) => callback.Invoke();
            worker.ProgressChanged += (sender, e) =>
                {
                    SimulationProgress simulationProgress = (SimulationProgress)e.UserState;
                    simulationProgress.Progress = e.ProgressPercentage;
                    progressCallback.Invoke(simulationProgress);
                };

           Action<int, double> sendProgressCallback = (progress, elapsedTime) =>
                {
                    Trace.WriteLine(string.Format("Simulation Progress: {0}", progress), "Network");
                    this.TotalElapsedTime = elapsedTime;
                    worker.ReportProgress(
                        progress,
                        new SimulationProgress { Bandwith = this.kilobytesToTransmit / this.TotalElapsedTime, ElapsedTime = this.TotalElapsedTime });
                };


            worker.RunWorkerAsync();

            worker.DoWork += (sender, e) =>
                {
                    double timeDelta = this.algorithm.FrameSizeInKilobytes / this.networkSpeedInKilobytesPerSeconds;

                    Trace.WriteLine(string.Format("Frame Count: {0}", this.algorithm.Frames.Count), "Network");

                    Frame frame = this.algorithm.Frames.Pop();

                    this.TotalElapsedTime = this.algorithm.Send(frame, this.numberOfClients, timeDelta, sendProgressCallback);

                    if (this.algorithm.WasTransmissionSuccessful(frame))
                    {
                        this.SuccesfulTransmissions++;
                    }
                    else
                    {
                        this.FailedTransmissions++;
                    }

                    worker.ReportProgress(
                       100,
                       new SimulationProgress { Bandwith = this.Bandwith, ElapsedTime = this.TotalElapsedTime });

                    Trace.WriteLine(string.Format("Effective Bandwith: {0}", this.Bandwith), "Network");
                };
        }


        private void OnSendProgressChanged(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }
    }
}